/*  This file is part of -_-.

    -_- is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    -_- is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with -_-.  If not, see <http://www.gnu.org/licenses/>.

    Copyright 2012 Oliver Katz */

#ifndef __VM_BACKEND_H
#define __VM_BACKEND_H

#include <iostream>
#include <string>
#include <vector>
#include <sstream>

#include <DerivedTypes.h>
#include <LLVMContext.h>
#include <Module.h>
#include <PassManager.h>
#include <Verifier.h>
#include <Passes.h>
#include <ExecutionEngine.h>
#include <Transforms/Scalar.h>
#include <IRBuilder.h>
#include <TargetSelect.h>
#include <JIT.h>

namespace sleepyface
{
	namespace vm
	{
		using namespace std;
		using namespace llvm;

		class VM;

		class LiteralType
		{
		private:
			int val;
			int elem;

		public:
			const static int _uninitialized = -1;
			const static int _char = 0;
			const static int _int = 1;
			const static int _double = 2;
			const static int _addr = 3;

			LiteralType();
			LiteralType(int v);
			LiteralType(int v, int e);
			bool initialized();
			int get();
			int getElement();
			Type *gen(VM &vm);
			string display(string pref);
			string display();
		};

		class LiteralChar
		{
		private:
			bool init;
			char val;

		public:
			LiteralChar();
			LiteralChar(char v);
			bool initialized();
			char get();
			Value *gen(VM &vm);
			string display(string pref);
			string display();
		};

		class LiteralInt
		{
		private:
			bool init;
			int val;

		public:
			LiteralInt();
			LiteralInt(int v);
			bool initialized();
			int get();
			Value *gen(VM &vm);
			string display(string pref);
			string display();
		};

		class LiteralDouble
		{
		private:
			bool init;
			double val;

		public:
			LiteralDouble();
			LiteralDouble(double v);
			bool initialized();
			double get();																																																																																																																																																																																																																				
			Value *gen(VM &vm);
			string display(string pref);
			string display();
		};

		class LiteralAddress
		{
		private:
			bool init;
			LiteralType elem;
			void *val;

		public:
			LiteralAddress();
			LiteralAddress(void *v);
			LiteralAddress(LiteralType e, void *v);
			bool initialized();
			LiteralType getElementType();
			void *get();
			Value *gen(VM &vm);
			string display(string pref);
			string display();
		};

		class LiteralValue
		{
		private:
			shared_ptr<LiteralType> type;
			shared_ptr<LiteralChar> _char;
			shared_ptr<LiteralInt> _int;
			shared_ptr<LiteralDouble> _double;
			shared_ptr<LiteralAddress> _addr;
			string name;

		public:
			LiteralValue();
			LiteralValue(LiteralChar v);
			LiteralValue(LiteralInt v);
			LiteralValue(LiteralDouble v);
			LiteralValue(LiteralAddress v);
			LiteralValue(string v);
			bool initialized();
			bool isNamedVariable();
			string getName();
			LiteralType getType();
			LiteralChar getChar();
			LiteralInt getInt();
			LiteralDouble getDouble();
			LiteralAddress getAddress();
			LiteralChar castChar();
			LiteralInt castInt();
			LiteralDouble castDouble();
			LiteralAddress castAddress();
			Value *gen(VM &vm);
			string display(string pref);
			string display();
		};

		class MutableDefinition;
		class MutableAssignment;
		class Operation;
		class Call;
		class IfStatement;
		class ForLoop;

		class FunctionalNode
		{
		public:
			const static int _uninitialized = -1;
			const static int _mut_definition = 0;
			const static int _mut_assign = 1;
			const static int _value = 2;
			const static int _arith = 3;
			const static int _call = 4;
			const static int _if = 5;
			const static int _forl = 6;

		private:
			int type;

			shared_ptr<MutableDefinition> md;
			shared_ptr<MutableAssignment> ma;
			shared_ptr<LiteralValue> val;
			shared_ptr<Operation> arith;
			shared_ptr<Call> call;
			shared_ptr<IfStatement> ifs;
			shared_ptr<ForLoop> forl;

		public:
			FunctionalNode();
			FunctionalNode(MutableDefinition d);
			FunctionalNode(MutableAssignment a);
			FunctionalNode(LiteralValue v);
			FunctionalNode(Operation o);
			FunctionalNode(Call c);
			FunctionalNode(IfStatement i);
			FunctionalNode(ForLoop f);
			bool initialized();
			int getType();
			bool proofOfDouble();
			string getTypeString();
			MutableDefinition getMutableDefinition();
			MutableAssignment getMutableAssignment();
			LiteralValue getValue();
			Operation getOperation();
			Call getCall();
			IfStatement getIfStatement();
			ForLoop getForLoop();
			Value *gen(VM &vm);
			string display(string pref);
			string display();
		};

		class MutableDefinition
		{
		private:
			LiteralType type;
			string name;
			FunctionalNode init;

		public:
			MutableDefinition();
			MutableDefinition(LiteralType t, string n);
			MutableDefinition(LiteralType t, string n, FunctionalNode v);
			bool initialized();
			LiteralType getType();
			string getName();
			FunctionalNode getValue();
			Value *gen(VM &vm);
			string display(string pref);
			string display();
		};

		class MutableAssignment
		{
		private:
			string name;
			FunctionalNode val;

		public:
			MutableAssignment();
			MutableAssignment(string n, FunctionalNode v);
			bool initialized();
			string getName();
			FunctionalNode getValue();
			Value *gen(VM &vm);
			string display(string pref);
			string display();
		};

		class Operation
		{
		private:
			int op;
			FunctionalNode left;
			FunctionalNode right;

		public:
			const static int uninitialized = -1;
			const static int iadd = 0;
			const static int fadd = 1;
			const static int isub = 2;
			const static int fsub = 3;
			const static int imul = 4;
			const static int fmul = 5;
			const static int idiv = 6;
			const static int fdiv = 7;
			const static int iand = 8;
			const static int ior = 9;
			const static int ixor = 10;
			const static int ilt = 11;
			const static int flt = 12;
			const static int igt = 13;
			const static int fgt = 14;
			const static int ile = 15;
			const static int fle = 16;
			const static int ige = 17;
			const static int fge = 18;
			const static int ieq = 19;
			const static int feq = 20;
			const static int ine = 21;
			const static int fne = 22;
			const static int ineg = 23;
			const static int icast = 24;
			const static int fcast = 25;
			const static int ideref8 = 26;
			const static int ideref32 = 27;
			const static int fderef32 = 28;
			const static int xaddr = 29;

			Operation();
			Operation(FunctionalNode l, int o, FunctionalNode r);
			Operation(int o, FunctionalNode r);
			bool initialized();
			int getOp();
			FunctionalNode getLeft();
			FunctionalNode getRight();
			Value *gen(VM &vm);
			string display(string pref);
			string display();
		};

		class Call
		{
		private:
			string func;
			vector<FunctionalNode> args;

		public:
			Call();
			Call(string f, vector<FunctionalNode> a);
			bool initialized();
			string getName();
			Value *gen(VM &vm);
			string display();
			string display(string pref);
		};

		class IfStatement
		{
		private:
			bool init;
			FunctionalNode cond;
			FunctionalNode then;
			FunctionalNode other;

		public:
			IfStatement();
			IfStatement(FunctionalNode c, FunctionalNode t, FunctionalNode o);
			bool initialized();
			FunctionalNode getCondition();
			FunctionalNode getThen();
			FunctionalNode getElse();
			Value *gen(VM &vm);
			string display(string pref);
			string display();
		};

		class ForLoop
		{
		private:
			string var;
			FunctionalNode init;
			FunctionalNode end;
			FunctionalNode step;
			FunctionalNode body;

		public:
			ForLoop();
			ForLoop(string v, FunctionalNode i, FunctionalNode e, FunctionalNode s, FunctionalNode b);
			bool initialized();
			string getVariable();
			FunctionalNode getInitial();
			FunctionalNode getFinal();
			FunctionalNode getStep();
			FunctionalNode getBody();
			Value *gen(VM &vm);
			string display(string pref);
			string display();
		};

		class Prototype
		{
		private:
			LiteralType rtn;
			vector<pair<LiteralType, string> > args;
			string name;

		public:
			Prototype();
			Prototype(string n, LiteralType r, vector<pair<LiteralType, string> > a);
			bool initialized();
			unsigned int size();
			LiteralType getReturnType();
			string getName();
			string getArgumentName(unsigned int idx);
			LiteralType getArgumentType(unsigned int idx);
			Function *gen(VM &vm);
			string display(string pref);
			string display();
		};

		class Body
		{
		private:
			Prototype ptype;
			vector<FunctionalNode> nodes;

		public:
			Body();
			Body(Prototype p);
			bool initialized();
			Prototype getPrototype();
			int appendFunctionalNode(FunctionalNode n);
			Function *gen(VM &vm);
			string display(string pref);
			string display();
		};

		class Assertion
		{
		private:
			
		};

		class VM
		{
		public:
			shared_ptr<Module> module;
			shared_ptr<IRBuilder<> > builder;
			shared_ptr<FunctionPassManager> fpm;
			ExecutionEngine *ee;
			map<string, AllocaInst *> variables;

			map<string, Function *> funcs;

			AllocaInst *createEntryBlockAlloca(Function *f, string n, Type *t);
			Value *loadMutable(string n);
			Value *storeMutable(string n, Value *v);

			VM();
			~VM();
			int setOptimizationLevel(int l);
			int linkExternal(Prototype p, void *f);
			void *JIT(Body b, bool d);
			void *JIT(Body b);
			vector<string> listJITedFunctions();
			int unJIT(string n);
		};
	}
}

#endif